/**
 * @license MIT or GPL-2.0
 * @fileOverview Favico animations
 * @author Miroslav Magda, http://blog.ejci.net
 * @source: https://github.com/ejci/favico.js
 * @version 0.3.10
 */

/**
 * Create new favico instance
 * @param {Object} Options
 * @return {Object} Favico object
 * @example
 * var favico = new Favico({
 *    bgColor : '#d00',
 *    textColor : '#fff',
 *    fontFamily : 'sans-serif',
 *    fontStyle : 'bold',
 *    type : 'circle',
 *    position : 'down',
 *    animation : 'slide',
 *    elementId: false,
 *    element: null,
 *    dataUrl: function(url){},
 *    win: window
 * });
 */
(function () {

  var Favico = (function (opt) {
    'use strict';
    opt = (opt) ? opt : {};
    var _def = {
      bgColor: '#d00',
      textColor: '#fff',
      fontFamily: 'sans-serif', //Arial,Verdana,Times New Roman,serif,sans-serif,...
      fontStyle: 'bold', //normal,italic,oblique,bold,bolder,lighter,100,200,300,400,500,600,700,800,900
      type: 'circle',
      position: 'down', // down, up, left, leftup (upleft)
      animation: 'slide',
      elementId: false,
      element: null,
      dataUrl: false,
      win: window
    };
    var _opt, _orig, _h, _w, _canvas, _context, _img, _ready, _lastBadge, _running, _readyCb, _stop, _browser, _animTimeout, _drawTimeout, _doc;

    _browser = {};
    _browser.ff = typeof InstallTrigger != 'undefined';
    _browser.chrome = !!window.chrome;
    _browser.opera = !!window.opera || navigator.userAgent.indexOf('Opera') >= 0;
    _browser.ie = /*@cc_on!@*/false;
    _browser.safari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
    _browser.supported = (_browser.chrome || _browser.ff || _browser.opera);

    var _queue = [];
    _readyCb = function () {
    };
    _ready = _stop = false;
    /**
     * Initialize favico
     */
    var init = function () {
      //merge initial options
      _opt = merge(_def, opt);
      _opt.bgColor = hexToRgb(_opt.bgColor);
      _opt.textColor = hexToRgb(_opt.textColor);
      _opt.position = _opt.position.toLowerCase();
      _opt.animation = (animation.types['' + _opt.animation]) ? _opt.animation : _def.animation;

      _doc = _opt.win.document;

      var isUp = _opt.position.indexOf('up') > -1;
      var isLeft = _opt.position.indexOf('left') > -1;

      //transform the animations
      if (isUp || isLeft) {
        for (var a in animation.types) {
          for (var i = 0; i < animation.types[a].length; i++) {
            var step = animation.types[a][i];

            if (isUp) {
              if (step.y < 0.6) {
                step.y = step.y - 0.4;
              } else {
                step.y = step.y - 2 * step.y + (1 - step.w);
              }
            }

            if (isLeft) {
              if (step.x < 0.6) {
                step.x = step.x - 0.4;
              } else {
                step.x = step.x - 2 * step.x + (1 - step.h);
              }
            }

            animation.types[a][i] = step;
          }
        }
      }
      _opt.type = (type['' + _opt.type]) ? _opt.type : _def.type;

      _orig = link. getIcons();
      //create temp canvas
      _canvas = document.createElement('canvas');
      //create temp image
      _img = document.createElement('img');
      var lastIcon = _orig[_orig.length - 1];
      if (lastIcon.hasAttribute('href')) {
        _img.setAttribute('crossOrigin', 'anonymous');
        //get width/height
        _img.onload = function () {
          _h = (_img.height > 0) ? _img.height : 32;
          _w = (_img.width > 0) ? _img.width : 32;
          _canvas.height = _h;
          _canvas.width = _w;
          _context = _canvas.getContext('2d');
          icon.ready();
        };
        _img.setAttribute('src', lastIcon.getAttribute('href'));
      } else {
        _h = 32;
        _w = 32;
        _img.height = _h;
        _img.width = _w;
        _canvas.height = _h;
        _canvas.width = _w;
        _context = _canvas.getContext('2d');
        icon.ready();
      }

    };
    /**
     * Icon namespace
     */
    var icon = {};
    /**
     * Icon is ready (reset icon) and start animation (if ther is any)
     */
    icon.ready = function () {
      _ready = true;
      icon.reset();
      _readyCb();
    };
    /**
     * Reset icon to default state
     */
    icon.reset = function () {
      //reset
      if (!_ready) {
        return;
      }
      _queue = [];
      _lastBadge = false;
      _running = false;
      _context.clearRect(0, 0, _w, _h);
      _context.drawImage(_img, 0, 0, _w, _h);
      //_stop=true;
      link.setIcon(_canvas);
      //webcam('stop');
      //video('stop');
      window.clearTimeout(_animTimeout);
      window.clearTimeout(_drawTimeout);
    };
    /**
     * Start animation
     */
    icon.start = function () {
      if (!_ready || _running) {
        return;
      }
      var finished = function () {
        _lastBadge = _queue[0];
        _running = false;
        if (_queue.length > 0) {
          _queue.shift();
          icon.start();
        } else {

        }
      };
      if (_queue.length > 0) {
        _running = true;
        var run = function () {
          // apply options for this animation
          ['type', 'animation', 'bgColor', 'textColor', 'fontFamily', 'fontStyle'].forEach(function (a) {
            if (a in _queue[0].options) {
              _opt[a] = _queue[0].options[a];
            }
          });
          animation.run(_queue[0].options, function () {
            finished();
          }, false);
        };
        if (_lastBadge) {
          animation.run(_lastBadge.options, function () {
            run();
          }, true);
        } else {
          run();
        }
      }
    };

    /**
     * Badge types
     */
    var type = {};
    var options = function (opt) {
      opt.n = ((typeof opt.n) === 'number') ? Math.abs(opt.n | 0) : opt.n;
      opt.x = _w * opt.x;
      opt.y = _h * opt.y;
      opt.w = _w * opt.w;
      opt.h = _h * opt.h;
      opt.len = ("" + opt.n).length;
      return opt;
    };
    /**
     * Generate circle
     * @param {Object} opt Badge options
     */
    type.circle = function (opt) {
      opt = options(opt);
      var more = false;
      if (opt.len === 2) {
        opt.x = opt.x - opt.w * 0.4;
        opt.w = opt.w * 1.4;
        more = true;
      } else if (opt.len >= 3) {
        opt.x = opt.x - opt.w * 0.65;
        opt.w = opt.w * 1.65;
        more = true;
      }
      _context.clearRect(0, 0, _w, _h);
      _context.drawImage(_img, 0, 0, _w, _h);
      _context.beginPath();
      _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.85 : 1)) + "px " + _opt.fontFamily;
      _context.textAlign = 'center';
      if (more) {
        _context.moveTo(opt.x + opt.w / 2, opt.y);
        _context.lineTo(opt.x + opt.w - opt.h / 2, opt.y);
        _context.quadraticCurveTo(opt.x + opt.w, opt.y, opt.x + opt.w, opt.y + opt.h / 2);
        _context.lineTo(opt.x + opt.w, opt.y + opt.h - opt.h / 2);
        _context.quadraticCurveTo(opt.x + opt.w, opt.y + opt.h, opt.x + opt.w - opt.h / 2, opt.y + opt.h);
        _context.lineTo(opt.x + opt.h / 2, opt.y + opt.h);
        _context.quadraticCurveTo(opt.x, opt.y + opt.h, opt.x, opt.y + opt.h - opt.h / 2);
        _context.lineTo(opt.x, opt.y + opt.h / 2);
        _context.quadraticCurveTo(opt.x, opt.y, opt.x + opt.h / 2, opt.y);
      } else {
        _context.arc(opt.x + opt.w / 2, opt.y + opt.h / 2, opt.h / 2, 0, 2 * Math.PI);
      }
      _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')';
      _context.fill();
      _context.closePath();
      _context.beginPath();
      _context.stroke();
      _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')';
      //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
      if ((typeof opt.n) === 'number' && opt.n > 999) {
        _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
      } else {
        _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
      }
      _context.closePath();
    };
    /**
     * Generate rectangle
     * @param {Object} opt Badge options
     */
    type.rectangle = function (opt) {
      opt = options(opt);
      var more = false;
      if (opt.len === 2) {
        opt.x = opt.x - opt.w * 0.4;
        opt.w = opt.w * 1.4;
        more = true;
      } else if (opt.len >= 3) {
        opt.x = opt.x - opt.w * 0.65;
        opt.w = opt.w * 1.65;
        more = true;
      }
      _context.clearRect(0, 0, _w, _h);
      _context.drawImage(_img, 0, 0, _w, _h);
      _context.beginPath();
      _context.font = _opt.fontStyle + " " + Math.floor(opt.h * (opt.n > 99 ? 0.9 : 1)) + "px " + _opt.fontFamily;
      _context.textAlign = 'center';
      _context.fillStyle = 'rgba(' + _opt.bgColor.r + ',' + _opt.bgColor.g + ',' + _opt.bgColor.b + ',' + opt.o + ')';
      _context.fillRect(opt.x, opt.y, opt.w, opt.h);
      _context.fillStyle = 'rgba(' + _opt.textColor.r + ',' + _opt.textColor.g + ',' + _opt.textColor.b + ',' + opt.o + ')';
      //_context.fillText((more) ? '9+' : opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
      if ((typeof opt.n) === 'number' && opt.n > 999) {
        _context.fillText(((opt.n > 9999) ? 9 : Math.floor(opt.n / 1000)) + 'k+', Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.2));
      } else {
        _context.fillText(opt.n, Math.floor(opt.x + opt.w / 2), Math.floor(opt.y + opt.h - opt.h * 0.15));
      }
      _context.closePath();
    };

    /**
     * Set badge
     */
    var badge = function (number, opts) {
      opts = ((typeof opts) === 'string' ? {
        animation: opts
      } : opts) || {};
      _readyCb = function () {
        try {
          if (typeof (number) === 'number' ? (number > 0) : (number !== '')) {
            var q = {
              type: 'badge',
              options: {
                n: number
              }
            };
            if ('animation' in opts && animation.types['' + opts.animation]) {
              q.options.animation = '' + opts.animation;
            }
            if ('type' in opts && type['' + opts.type]) {
              q.options.type = '' + opts.type;
            }
            ['bgColor', 'textColor'].forEach(function (o) {
              if (o in opts) {
                q.options[o] = hexToRgb(opts[o]);
              }
            });
            ['fontStyle', 'fontFamily'].forEach(function (o) {
              if (o in opts) {
                q.options[o] = opts[o];
              }
            });
            _queue.push(q);
            if (_queue.length > 100) {
              throw new Error('Too many badges requests in queue.');
            }
            icon.start();
          } else {
            icon.reset();
          }
        } catch (e) {
          throw new Error('Error setting badge. Message: ' + e.message);
        }
      };
      if (_ready) {
        _readyCb();
      }
    };

    /**
     * Set image as icon
     */
    var image = function (imageElement) {
      _readyCb = function () {
        try {
          var w = imageElement.width;
          var h = imageElement.height;
          var newImg = document.createElement('img');
          var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h);
          newImg.setAttribute('crossOrigin', 'anonymous');
          newImg.onload=function(){
            _context.clearRect(0, 0, _w, _h);
            _context.drawImage(newImg, 0, 0, _w, _h);
            link.setIcon(_canvas);
          };
          newImg.setAttribute('src', imageElement.getAttribute('src'));
          newImg.height = (h / ratio);
          newImg.width = (w / ratio);
        } catch (e) {
          throw new Error('Error setting image. Message: ' + e.message);
        }
      };
      if (_ready) {
        _readyCb();
      }
    };
    /**
     * Set the icon from a source url. Won't work with badges.
     */
    var rawImageSrc = function (url) {
      _readyCb = function() {
        link.setIconSrc(url);
      };
      if (_ready) {
        _readyCb();
      }
    };
    /**
     * Set video as icon
     */
    var video = function (videoElement) {
      _readyCb = function () {
        try {
          if (videoElement === 'stop') {
            _stop = true;
            icon.reset();
            _stop = false;
            return;
          }
          //var w = videoElement.width;
          //var h = videoElement.height;
          //var ratio = (w / _w < h / _h) ? (w / _w) : (h / _h);
          videoElement.addEventListener('play', function () {
            drawVideo(this);
          }, false);

        } catch (e) {
          throw new Error('Error setting video. Message: ' + e.message);
        }
      };
      if (_ready) {
        _readyCb();
      }
    };
    /**
     * Set video as icon
     */
    var webcam = function (action) {
      //UR
      if (!window.URL || !window.URL.createObjectURL) {
        window.URL = window.URL || {};
        window.URL.createObjectURL = function (obj) {
          return obj;
        };
      }
      if (_browser.supported) {
        var newVideo = false;
        navigator.getUserMedia = navigator.getUserMedia || navigator.oGetUserMedia || navigator.msGetUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
        _readyCb = function () {
          try {
            if (action === 'stop') {
              _stop = true;
              icon.reset();
              _stop = false;
              return;
            }
            newVideo = document.createElement('video');
            newVideo.width = _w;
            newVideo.height = _h;
            navigator.getUserMedia({
              video: true,
              audio: false
            }, function (stream) {
              newVideo.src = URL.createObjectURL(stream);
              newVideo.play();
              drawVideo(newVideo);
            }, function () {
            });
          } catch (e) {
            throw new Error('Error setting webcam. Message: ' + e.message);
          }
        };
        if (_ready) {
          _readyCb();
        }
      }

    };

    var setOpt = function (key, value) {
      var opts = key;
      if (!(value == null && Object.prototype.toString.call(key) == '[object Object]')) {
        opts = {};
        opts[key] = value;
      }

      var keys = Object.keys(opts);
      for (var i = 0; i < keys.length; i++) {
        if (keys[i] == 'bgColor' || keys[i] == 'textColor') {
          _opt[keys[i]] = hexToRgb(opts[keys[i]]);
        } else {
          _opt[keys[i]] = opts[keys[i]];
        }
      }

      _queue.push(_lastBadge);
      icon.start();
    };

    /**
     * Draw video to context and repeat :)
     */
    function drawVideo(video) {
      if (video.paused || video.ended || _stop) {
        return false;
      }
      //nasty hack for FF webcam (Thanks to Julian Ćwirko, kontakt@redsunmedia.pl)
      try {
        _context.clearRect(0, 0, _w, _h);
        _context.drawImage(video, 0, 0, _w, _h);
      } catch (e) {

      }
      _drawTimeout = setTimeout(function () {
        drawVideo(video);
      }, animation.duration);
      link.setIcon(_canvas);
    }

    var link = {};
    /**
     * Get icons from HEAD tag or create a new <link> element
     */
    link.getIcons = function () {
      var elms = [];
      //get link element
      var getLinks = function () {
        var icons = [];
        var links = _doc.getElementsByTagName('head')[0].getElementsByTagName('link');
        for (var i = 0; i < links.length; i++) {
          if ((/(^|\s)icon(\s|$)/i).test(links[i].getAttribute('rel'))) {
            icons.push(links[i]);
          }
        }
        return icons;
      };
      if (_opt.element) {
        elms = [_opt.element];
      } else if (_opt.elementId) {
        //if img element identified by elementId
        elms = [_doc.getElementById(_opt.elementId)];
        elms[0].setAttribute('href', elms[0].getAttribute('src'));
      } else {
        //if link element
        elms = getLinks();
        if (elms.length === 0) {
          elms = [_doc.createElement('link')];
          elms[0].setAttribute('rel', 'icon');
          _doc.getElementsByTagName('head')[0].appendChild(elms[0]);
        }
      }
      elms.forEach(function(item) {
        item.setAttribute('type', 'image/png');
      });
      return elms;
    };
    link.setIcon = function (canvas) {
      var url = canvas.toDataURL('image/png');
      link.setIconSrc(url);
    };
    link.setIconSrc = function (url) {
      if (_opt.dataUrl) {
        //if using custom exporter
        _opt.dataUrl(url);
      }
      if (_opt.element) {
        _opt.element.setAttribute('href', url);
        _opt.element.setAttribute('src', url);
      } else if (_opt.elementId) {
        //if is attached to element (image)
        var elm = _doc.getElementById(_opt.elementId);
        elm.setAttribute('href', url);
        elm.setAttribute('src', url);
      } else {
        //if is attached to fav icon
        if (_browser.ff || _browser.opera) {
          //for FF we need to "recreate" element, atach to dom and remove old <link>
          //var originalType = _orig.getAttribute('rel');
          var old = _orig[_orig.length - 1];
          var newIcon = _doc.createElement('link');
          _orig = [newIcon];
          //_orig.setAttribute('rel', originalType);
          if (_browser.opera) {
            newIcon.setAttribute('rel', 'icon');
          }
          newIcon.setAttribute('rel', 'icon');
          newIcon.setAttribute('type', 'image/png');
          _doc.getElementsByTagName('head')[0].appendChild(newIcon);
          newIcon.setAttribute('href', url);
          if (old.parentNode) {
            old.parentNode.removeChild(old);
          }
        } else {
          _orig.forEach(function(icon) {
            icon.setAttribute('href', url);
          });
        }
      }
    };

    //http://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb#answer-5624139
    //HEX to RGB convertor
    function hexToRgb(hex) {
      var shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
      hex = hex.replace(shorthandRegex, function (m, r, g, b) {
        return r + r + g + g + b + b;
      });
      var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
      return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
      } : false;
    }

    /**
     * Merge options
     */
    function merge(def, opt) {
      var mergedOpt = {};
      var attrname;
      for (attrname in def) {
        mergedOpt[attrname] = def[attrname];
      }
      for (attrname in opt) {
        mergedOpt[attrname] = opt[attrname];
      }
      return mergedOpt;
    }

    /**
     * Cross-browser page visibility shim
     * http://stackoverflow.com/questions/12536562/detect-whether-a-window-is-visible
     */
    function isPageHidden() {
      return _doc.hidden || _doc.msHidden || _doc.webkitHidden || _doc.mozHidden;
    }

    /**
     * @namespace animation
     */
    var animation = {};
    /**
     * Animation "frame" duration
     */
    animation.duration = 40;
    /**
     * Animation types (none,fade,pop,slide)
     */
    animation.types = {};
    animation.types.fade = [{
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 0.0
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 0.1
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 0.2
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 0.3
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 0.4
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 0.5
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 0.6
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 0.7
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 0.8
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 0.9
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 1.0
    }];
    animation.types.none = [{
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 1
    }];
    animation.types.pop = [{
      x: 1,
      y: 1,
      w: 0,
      h: 0,
      o: 1
    }, {
      x: 0.9,
      y: 0.9,
      w: 0.1,
      h: 0.1,
      o: 1
    }, {
      x: 0.8,
      y: 0.8,
      w: 0.2,
      h: 0.2,
      o: 1
    }, {
      x: 0.7,
      y: 0.7,
      w: 0.3,
      h: 0.3,
      o: 1
    }, {
      x: 0.6,
      y: 0.6,
      w: 0.4,
      h: 0.4,
      o: 1
    }, {
      x: 0.5,
      y: 0.5,
      w: 0.5,
      h: 0.5,
      o: 1
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 1
    }];
    animation.types.popFade = [{
      x: 0.75,
      y: 0.75,
      w: 0,
      h: 0,
      o: 0
    }, {
      x: 0.65,
      y: 0.65,
      w: 0.1,
      h: 0.1,
      o: 0.2
    }, {
      x: 0.6,
      y: 0.6,
      w: 0.2,
      h: 0.2,
      o: 0.4
    }, {
      x: 0.55,
      y: 0.55,
      w: 0.3,
      h: 0.3,
      o: 0.6
    }, {
      x: 0.50,
      y: 0.50,
      w: 0.4,
      h: 0.4,
      o: 0.8
    }, {
      x: 0.45,
      y: 0.45,
      w: 0.5,
      h: 0.5,
      o: 0.9
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 1
    }];
    animation.types.slide = [{
      x: 0.4,
      y: 1,
      w: 0.6,
      h: 0.6,
      o: 1
    }, {
      x: 0.4,
      y: 0.9,
      w: 0.6,
      h: 0.6,
      o: 1
    }, {
      x: 0.4,
      y: 0.9,
      w: 0.6,
      h: 0.6,
      o: 1
    }, {
      x: 0.4,
      y: 0.8,
      w: 0.6,
      h: 0.6,
      o: 1
    }, {
      x: 0.4,
      y: 0.7,
      w: 0.6,
      h: 0.6,
      o: 1
    }, {
      x: 0.4,
      y: 0.6,
      w: 0.6,
      h: 0.6,
      o: 1
    }, {
      x: 0.4,
      y: 0.5,
      w: 0.6,
      h: 0.6,
      o: 1
    }, {
      x: 0.4,
      y: 0.4,
      w: 0.6,
      h: 0.6,
      o: 1
    }];
    /**
     * Run animation
     * @param {Object} opt Animation options
     * @param {Object} cb Callabak after all steps are done
     * @param {Object} revert Reverse order? true|false
     * @param {Object} step Optional step number (frame bumber)
     */
    animation.run = function (opt, cb, revert, step) {
      var animationType = animation.types[isPageHidden() ? 'none' : _opt.animation];
      if (revert === true) {
        step = (typeof step !== 'undefined') ? step : animationType.length - 1;
      } else {
        step = (typeof step !== 'undefined') ? step : 0;
      }
      cb = (cb) ? cb : function () {
      };
      if ((step < animationType.length) && (step >= 0)) {
        type[_opt.type](merge(opt, animationType[step]));
        _animTimeout = setTimeout(function () {
          if (revert) {
            step = step - 1;
          } else {
            step = step + 1;
          }
          animation.run(opt, cb, revert, step);
        }, animation.duration);

        link.setIcon(_canvas);
      } else {
        cb();
        return;
      }
    };
    //auto init
    init();
    return {
      badge: badge,
      video: video,
      image: image,
      rawImageSrc: rawImageSrc,
      webcam: webcam,
      setOpt: setOpt,
      reset: icon.reset,
      browser: {
        supported: _browser.supported
      }
    };
  });

  // AMD / RequireJS
  if (typeof define !== 'undefined' && define.amd) {
    define([], function () {
      return Favico;
    });
  }
  // CommonJS
  else if (typeof module !== 'undefined' && module.exports) {
    module.exports = Favico;
  }
  // included directly via <script> tag
  else {
    this.Favico = Favico;
  }

})();
